package org.moonlight.jvm.classfile.attribute;

import org.moonlight.jvm.classfile.ClassFile;
import org.moonlight.jvm.classfile.ClassReader;
import org.moonlight.jvm.classfile.attribute.debuginfo.LineNumberTableAttribute;
import org.moonlight.jvm.classfile.attribute.debuginfo.LocalVariableTableAttribute;
import org.moonlight.jvm.classfile.attribute.debuginfo.LocalVariableTypeTableAttribute;
import org.moonlight.jvm.classfile.attribute.debuginfo.SourceFileAttribute;
import org.moonlight.jvm.classfile.attribute.mark.DeprecatedAttribute;
import org.moonlight.jvm.classfile.attribute.mark.SyntheticAttribute;
import org.moonlight.jvm.classfile.constant.StaticConstantPool;

/**
 * 属性信息接口。和常量池类似，各种属性表达的信息也各不相同，因此无法用统一的结构来定义。
 * 不同之处在于，常量是由Java虚拟机规范严格定义的，共有14种。但属性是可以扩展的，
 * 不同的虚拟机实现可以定义自己的属性类型。由于这个原因，Java虚拟机规范没有使用tag，而是使用属性名来区别不同的属性。
 * 属性数据放在属性名之后的u1表中，这样Java虚拟机实现就可以跳过自己无法识别的属性。
 *
 * 属性的结构定义如下:
 *   attribute_info {
 *       u2  attribute_name_index
 *       u4  attribute_length
 *       u1  info[attribute_length]
 *   }
 *  注意，属性表中存放的属性名实际上并不是编码后的字符串，而是常量池索引，指向常量池中的CONSTANT_Utf8_info常量。
 *
 *  按照用途，23种预定义属性可以分为三组。
 *    1. 第一组属性是实现Java虚拟机所必需的，共有5种；
 *    2. 第二组属性是Java类库所必需的，共有12种；
 *    3. 第三组属性主要提供给工具使用，共有6种。
 *  第三组属性是可选的，也就是说可以不出现在class文件中。如果class文件中存在第三组属性，
 *  Java虚拟机实现或者Java类库也是可以利用它们的，比如使用LineNumberTable属性在异常堆栈中显示行号。
 *
 *  从class文件演进的角度来讲，
 *    1. JDK1.0 时只有6种预定义属性，
 *    2. JDK1.1 增加了3种。
 *    3. J2SE 5.0 增加了9种属性，主要用于支持泛型和注解。
 *    4. Java SE 6增加了 StackMapTable 属性，用于优化字节码验证。
 *    5. Java SE 7增加了 BootstrapMethods 属性，用于支持新增的invokedynamic指令。
 *    6. Java SE 8又增加了 RuntimeInvisibleAnnotations, RuntimeVisibleAnnotations, MethodParameters 三种属性。
 *
 *  预定义属性表:
 *    属性名                                 JavaSE              分组                位置
 *    ConstantValue                         1.0.2                1                 field_info
 *    Code                                  1.0.2                1                 method_info
 *    Exceptions                            1.0.2                1                 method_info
 *    SourceFile                            1.0.2                3                 ClassFile
 *    LineNumberTable                       1.0.2                3                 Code
 *    LocalVariableTable                    1.0.2                3                 Code
 *    InnerClasses                          1.1                  2                 ClassFile
 *    Synthetic                             1.1                  2                 ClassFile, field_info, method_info
 *    Deprecated                            1.1                  3                 ClassFile, field_info, method_info
 *    EnclosingMethod                       5.0                  2                 ClassFile
 *    Signature                             5.0                  2                 ClassFile, field_info, method_info
 *    SourceDebugExtension                  5.0                  3                 ClassFile
 *    LocalVariableTypeTable                5.0                  3                 Code
 *    RuntimeVisibleAnnotations             5.0                  2                 ClassFile, field info, method info
 *    RuntimeInvisibleAnnotations           5.0                  2                 ClassFile, field info, method info
 *    RuntimeVisibleParameterAnnotations    5.0                  2                 method_info
 *    RuntimeInvisibleParameterAnnotations  5.0                  2                 method_info
 *    AnnotationDefault                     5.0                  2                 method_info
 *    StackMapTable                         6                    1                 Code
 *    BootstrapMethods                      7                    1                 ClassFile
 *    RuntimeVisibleTypeAnnotations         8                    2                 ClassFile, field_info, method_info, Code
 *    RuntimeInvisibleTypeAnnotations       8                    2                 ClassFile, field_info, method_info, Code
 *    MethodParameters                      8                    2                 method_info
 *
 * @author Moonlight
 * @createTime 2023/8/15 9:49
 **/
public interface AttributeInfo {

    /** 
     * 使用 类文件数据读取解析器{@link ClassReader} 从 类文件数据{@link ClassFile} 读取解析出 属性信息{@link AttributeInfo}
     *
     * @param reader 类文件数据读取解析器
     * @createTime 10:03 2023/8/15 
     * @author moonlight
     **/
    void readInfo(ClassReader reader);

    /**
     * 使用 类文件数据读取解析器{@link ClassReader} 从 类文件数据{@link ClassFile} 读取解析出 属性信息{@link AttributeInfo}数组
     *
     * @param reader 类文件数据读取解析器
     * @param scp 类文件静态常量池 {@link StaticConstantPool}
     * @return AttributeInfo[] 属性信息数组
     * @createTime 10:07 2023/8/15
     * @author moonlight
     **/
    static AttributeInfo[] readAttributes(ClassReader reader, StaticConstantPool scp) {
        int attrCnt = reader.readUint16();
        AttributeInfo[] attrs = new AttributeInfo[attrCnt];
        for (int i = 0; i < attrCnt; i++) {
            attrs[i] = readAttr(reader, scp);
        }
        return attrs;
    }

    /**
     * 使用 类文件数据读取解析器{@link ClassReader} 从 类文件数据{@link ClassFile} 读取解析出 单个属性信息{@link AttributeInfo}
     *
     * @param reader 类文件数据读取解析器
     * @param scp 类文件静态常量池 {@link StaticConstantPool}
     * @return AttributeInfo 属性信息
     * @createTime 10:17 2023/8/15
     * @author moonlight
     **/
    static AttributeInfo readAttr(ClassReader reader, StaticConstantPool scp) {
        int nameIdx = reader.readUint16();
        String name = scp.getUtf8(nameIdx);
        int attrLen = reader.readUint32ToInteger();
        AttributeInfo attrInfo = newAttributeInfo(name, attrLen, scp);
        attrInfo.readInfo(reader);
        return attrInfo;
    }

    /**
     * 从 类文件中的静态常量池 {@link StaticConstantPool} 中读取数据 根据 属性数据名称 属性数据长度
     * 创建 具体的 属性信息对象
     *
     * @param name 属性名称
     * @param attrLen 属性长度
     * @param scp 类文件静态常量池 {@link StaticConstantPool}
     * @return AttributeInfo
     * @createTime 15:48 2023/8/15
     * @author moonlight
     **/
    static AttributeInfo newAttributeInfo(String name, int attrLen, StaticConstantPool scp) {
        switch (name) {
            case "BootstrapMethods":
                return new BootstrapMethodsAttribute();
            case "Code":
                return new CodeAttribute(scp);
            case "ConstantValue":
                return new ConstantValueAttribute();
            case "Deprecated":
                return new DeprecatedAttribute();
            case "EnclosingMethod":
                return new EnclosingMethodAttribute(scp);
            case "Exceptions":
                return new ExceptionsAttribute();
            case "InnerClasses":
                return new InnerClassesAttribute();
            case "LineNumberTable":
                return new LineNumberTableAttribute();
            case "LocalVariableTable":
                return new LocalVariableTableAttribute();
            case "LocalVariableTypeTable":
                return new LocalVariableTypeTableAttribute();
            // case "MethodParameters":
            // case "RuntimeInvisibleAnnotations":
            // case "RuntimeInvisibleParameterAnnotations":
            // case "RuntimeInvisibleTypeAnnotations":
            // case "RuntimeVisibleAnnotations":
            // case "RuntimeVisibleParameterAnnotations":
            // case "RuntimeVisibleTypeAnnotations":
            case "Signature":
                return new SignatureAttribute(scp);
            case "SourceFile":
                return new SourceFileAttribute(scp);
            // case "SourceDebugExtension":
            // case "StackMapTable":
            case "Synthetic":
                return new SyntheticAttribute();
            default:
                return new UnparsedAttribute(name, attrLen);
        }
    }

}